const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
function hex2b64(str) {
	let result = '';
	let previous = 0;
	let remainder = 0;
	for (let i = 0; i + 1 < str.length; i += 2) {
		const v = parseInt(str.substring(i, i + 2), 16);
		const used = 6 - remainder;
		const idx = (((previous << (8 - remainder)) & 0xff) >> 2) | (v >> (8 - used));
		result += b64.charAt(idx);
		remainder = 8 - used;
		if (remainder === 6) {
			result += b64.charAt(((v << (8 - remainder)) & 0xff) >> 2);
			remainder = 0;
		} else {
			previous = v;
		}
	}
	result += b64.charAt(((previous << (8 - remainder)) & 0xff) >> 2);
	return result + ''.padEnd(4 - (result.length % 4), '=');
}
console.log(hex2b64('616263e4b8ade6968731323334'));

const b64Arr = [
	65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99,
	100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48,
	49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47,
];

function hex2b64Ex(str) {
	// let result = '';
	let previous = 0;
	let remainder = 0;
	let pos = 0;
	let len = Math.ceil(((str.length / 2) * 8) / 6);
	len += 4 - (len % 4);
	const buf = new Uint8Array(len);
	for (let i = 0; i + 1 < str.length; i += 2) {
		const v = parseInt(str.substring(i, i + 2), 16);
		const used = 6 - remainder;
		const idx = (((previous << (8 - remainder)) & 0xff) >> 2) | (v >> (8 - used));
		buf[pos++] = b64Arr[idx];
		remainder = 8 - used;
		if (remainder === 6) {
			const idx = ((v << (8 - remainder)) & 0xff) >> 2;
			buf[pos++] = b64Arr[idx];
			remainder = 0;
		} else {
			previous = v;
		}
	}
	const idx = ((previous << (8 - remainder)) & 0xff) >> 2;
	buf[pos++] = b64Arr[idx];
	for (; pos < len; pos++) {
		buf[pos] = '='.charCodeAt(0);
	}
	let utf8decoder = new TextDecoder('ascii');
	return utf8decoder.decode(buf);
}
console.log(hex2b64Ex('616263e4b8ade6968731323334'));
